﻿using ArtMath.Attribute;
using ArtMath.Core.Converter;
using ArtMath.Core.Helper;
using ArtMath.CommonType;
using ArtMath.Resource;
using System;
using System.ComponentModel;

namespace ArtMath.Core.Data
{
    /// <summary>
    /// 不含参数的带值表达式
    /// </summary>
    [Serializable, TypeConverter(typeof(StringXConverter))]
    public class StringX : IFormatPainter<StringX>, IStringSettable
    {
        string directExpression;
        [Browsable(false)]
        public string StandardizedExpression { get; protected set; }
        protected double val;
        public event Action<StringX> ExpressionSetting;
        public event Action<StringX, ExpressionSetEventArgs> ExpressionSet;
        public StringX()
        {

        }
        public StringX(string expression)
        {
            SetExpression(expression, false);
        }
        public StringX(double value)
        {
            SetExpression(value, false);
        }
        public static implicit operator StringX(string exp)
        {
            return new StringX(exp);
        }
        public static implicit operator StringX(double value)
        {
            return new StringX(value);
        }
        [Browsable(false)]
        public virtual string DirectExpression
        {
            get { return directExpression; }
            set
            {
                directExpression = value;
                StandardizedExpression = Utility.StandardizePolynomial(directExpression);
            }
        }
        /// <summary>
        /// 指示当表达式改变或参数值改变后能否立即算出结果
        /// </summary>
        [Browsable(false)]
        protected virtual bool CanCalc
        {
            get { return true; }
        }
        /// <summary>
        /// 设置表达式为一个数字
        /// </summary>
        /// <param name="value"></param>
        /// <param name="raiseEvent">是否触发表达式设置事件</param>
        public virtual void SetExpression(double value, bool raiseEvent = true)
        {
            if (raiseEvent)
            {
                ExpressionSetting?.Invoke(this);
            }
            double val_bkp = Value;
            string str_bkp = DirectExpression;
            Value = value;
            StandardizedExpression = directExpression = Value.ToString();
            setDouble();
            if (raiseEvent)
            {
                ExpressionSet?.Invoke(this, new ExpressionSetEventArgs(val_bkp, str_bkp));
            }
        }
        protected virtual void setDouble()
        {

        }
        /// <summary>
        /// 设置表达式，计算表达式的值，并触发表达式设置事件
        /// </summary>
        /// <param name="value"></param>
        public virtual void SetExpression(string value, bool raiseEvent = true, bool showMsg = true)
        {
            if (string.IsNullOrEmpty(value))
            {
                SetExpression(0, raiseEvent);
            }
            else
            {
                if (raiseEvent)
                {
                    ExpressionSetting?.Invoke(this);
                }
                double val_bkp = Value;
                string str_bkp = DirectExpression;
                StandardizedExpression = Utility.StandardizePolynomial(value);
                if (setString(value, StandardizedExpression, showMsg))
                {
                    directExpression = value;
                }
                else
                {
                    DirectExpression = str_bkp;
                    Value = val_bkp;
                }
                if (raiseEvent)
                {
                    ExpressionSet?.Invoke(this, new ExpressionSetEventArgs(val_bkp, str_bkp));
                }
            }
        }
        protected virtual bool setString(string origin, string std, bool showMsg)
        {
            if (CanCalc)
            {
                if (!Calculation.Calculate(std, out val))
                {
                    if (!showMsg)
                    {
                        throw new ArgumentException(string.Format(Resources.Translate("InvalidExpression"), origin));
                    }
                    return false;
                }
            }
            return true;
        }
        [MultilingualDisplayName("Value"), ReadOnly(true)]
        public double Value
        {
            get { return val; }
            set { val = value; }
        }
        [Browsable(false)]
        public float FloatValue
        {
            get { return (float)Value; }
        }
        public override string ToString()
        {
            return DirectExpression;
        }
        public void UseFormat(StringX obj)
        {
            SetExpression(obj.DirectExpression);
        }
        public void SetFromString(string exp)
        {
            SetExpression(exp);
        }
        public static double operator -(StringX right)
        {
            return -right.Value;
        }
        public static double operator +(StringX left, StringX right)
        {
            return left.Value + right.Value;
        }
        public static double operator -(StringX left, StringX right)
        {
            return left.Value - right.Value;
        }
        public static double operator *(StringX left, StringX right)
        {
            return left.Value * right.Value;
        }
        public static double operator /(StringX left, StringX right)
        {
            return left.Value / right.Value;
        }
        public static double operator %(StringX left, StringX right)
        {
            return left.Value % right.Value;
        }
        public static bool operator >(StringX left, StringX right)
        {
            return left.Value > right.Value;
        }
        public static bool operator >=(StringX left, StringX right)
        {
            return left > right || left == right;
        }
        public static bool operator <(StringX left,StringX right)
        {
            return left.Value < right.Value;
        }
        public static bool operator <=(StringX left, StringX right)
        {
            return left < right || left == right;
        }
        public static bool operator ==(StringX left, StringX right)
        {
            return left.Value == right.Value;
        }
        public static bool operator !=(StringX left, StringX right)
        {
            return !(left == right);
        }
    }
    public class ExpressionSetEventArgs : EventArgs
    {
        public double OldValue { get; }
        public string OldExpression { get; }
        public ExpressionSetEventArgs(double oldValue, string oldExpression)
        {
            OldValue = oldValue;
            OldExpression = oldExpression;
        }
    }
}
